import 'package:flutter/material.dart';

import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'gt_list_state.g.dart';

@immutable
class GTListState {
  final bool isLoading;
  final List<dynamic> list;
  final List<dynamic> newList;
  final bool hasMoreData;
  final int currentPage;
  final int totalCount;
  final String? errorMessage;
  final GTListConfig config;

  const GTListState({
    required this.config,
    this.isLoading = true,
    this.list = const [],
    this.newList = const [],
    this.currentPage = 0,
    this.hasMoreData = false,
    this.totalCount = 0,
    this.errorMessage,
  });

  GTListState copyWith({
    bool? isLoading,
    List<dynamic>? list,
    List<dynamic>? newList,
    bool? hasMoreData,
    int? currentPage,
    int? totalCount,
    dynamic response,
    String? errorMessage,
    GTListConfig? config,
  }) {
    return GTListState(
      isLoading: isLoading ?? this.isLoading,
      list: list ?? this.list,
      newList: newList ?? this.newList,
      hasMoreData: hasMoreData ?? this.hasMoreData,
      currentPage: currentPage ?? this.currentPage,
      totalCount: totalCount ?? this.totalCount,
      errorMessage: errorMessage,
      config: config ?? this.config,
    );
  }

  @override
  String toString() {
    return 'ListState{isLoading: $isLoading, hasMoreData: $hasMoreData, currentPage: $currentPage, totalCount: $totalCount, listCount: ${list.length}, newListCount: ${newList.length}, errorMessage: $errorMessage}';
  }
}

typedef ModelFetcher = Future<List<dynamic>?> Function(Map<String, String> pageParams);

@immutable
class GTListConfig {
  final bool isAutoJoinStartAndLimit; // 是否自动拼接Start和Limit参数，默认true
  final String currentPageKey;
  final bool useOffset;
  final int pageSize;
  final bool determineMoreDataByPageSize;
  final ModelFetcher modelFetcher;

  const GTListConfig({
    required this.modelFetcher,
    this.isAutoJoinStartAndLimit = true,
    this.useOffset = false,
    this.currentPageKey = 'page_index',
    this.pageSize = 20,
    this.determineMoreDataByPageSize = false,
  });
}

@riverpod
class GTListController extends _$GTListController {
  static const firstPageIndex = 0;
  static const defaultPageSize = 20;
  static const defaultPageIndexKey = 'page_index';
  static const defaultPageSizeKey = 'page_size';

  @override
  GTListState build({
    required GTListConfig config,
  }) {
    return GTListState(config: config);
  }

  Future<bool> requestFirstPage({Map<String, String>? params}) {
    return start(params, firstPageIndex, true);
  }

  Future<bool> requestNextPage({Map<String, String>? params}) {
    return start(params, state.currentPage + 1, false);
  }

  void insertModel(dynamic model, int index) {
    var list = List.from(state.list);
    if (list.length >= index) {
      list.insert(index, model);
      state = state.copyWith(list: list);
    }
  }

  void removeModelAt(int index) {
    var list = List.from(state.list);
    if (list.length > index) {
      list.removeAt(index);
      state = state.copyWith(list: list);
    }
  }

  void updateModel(dynamic model, int index) {
    var list = List.from(state.list);
    if (list.length > index) {
      list[index] = model;
      state = state.copyWith(list: list);
    }
  }

  void updateList(dataList) {
    state = state.copyWith(list: dataList);
  }

  /// 返回值: true: 有更多数据, false: 没有更多数据
  Future<bool> start(Map<String, String>? params, int page, bool isReload) async {
    if (!state.isLoading) {
      state = state.copyWith(isLoading: true);
    }

    Map<String, String> requestParams = params != null ? Map.from(params) : {};
    final config = state.config;
    if (config.isAutoJoinStartAndLimit) {
      if (config.useOffset) {
        requestParams[config.currentPageKey] = (page * config.pageSize).toString();
      } else {
        requestParams[config.currentPageKey] = page.toString();
      }
      requestParams[defaultPageSizeKey] = config.pageSize.toString();
    }
    List<dynamic>? newList;
    String? errorMessage;
    try {
      newList = await config.modelFetcher(requestParams);
    } catch (e) {
      errorMessage = e.toString();
    }
    if (newList != null) {
      state = state.copyWith(
        isLoading: false,
        list: isReload ? newList : [...state.list, ...newList],
        currentPage: page,
        hasMoreData: config.determineMoreDataByPageSize ? newList.length >= config.pageSize - 1 : newList.isNotEmpty,
        errorMessage: null,
      );
    } else {
      state = state.copyWith(isLoading: false, errorMessage: errorMessage);
    }
    return state.hasMoreData;
  }
}
